﻿using System;
using System.Collections;

namespace Quail.Silverlight
{
    /// <summary>
    /// General control container for silverlight controls
    /// </summary>
    public abstract class SlvControlContainer : IQuailControl
    {
        /// <summary>
        /// Dictionary of constructed controls
        /// </summary>
        protected readonly Hashtable HashControls = new Hashtable();

        private IQuailControl _parentControl;

        /// <summary>
        /// Initializes a new instance of the <see cref="SlvControlContainer"/> class.
        /// </summary>
        /// <param name="container">The control container.</param>
        protected SlvControlContainer(IQuailControl container)
        {
            ParentControl = container;
        }

        /// <summary>
        /// Fired when ParentControl changes values
        /// </summary>
        public event EventHandler ParentControlChanged;

        /// <summary>
        /// Gets a value indicating whether this <see cref="IQuailControl"/> is visible.
        /// </summary>
        /// <value><c>true</c> if visible; otherwise, <c>false</c>.</value>
        public abstract bool Visible { get; }

        /// <summary>
        /// Gets a value indicating whether this <see cref="IQuailControl"/> is enabled.
        /// </summary>
        /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
        public abstract bool Enabled { get; }

        /// <summary>
        /// Gets a value indicating whether this <see cref="IQuailControl"/> is exists.
        /// </summary>
        /// <value><c>true</c> if exists; otherwise, <c>false</c>.</value>
        public abstract bool Exists { get; }

        /// <summary>
        /// Gets or sets the selector.
        /// </summary>
        /// <value>The selector.</value>
        public abstract string Selector { get; set; }

        /// <summary>
        /// Gets the page.
        /// </summary>
        /// <value>The page.</value>
        public abstract WebPage Page { get; }

        /// <summary>
        /// Gets the browser.
        /// </summary>
        /// <value>The browser.</value>
        public abstract WebBrowser Browser { get; }

        /// <summary>
        /// Gets the active task.
        /// </summary>
        /// <value>The active task.</value>
        public abstract Task ActiveTask { get; }

        /// <summary>
        /// Gets or sets the ParentControl for this control
        /// </summary>
        public IQuailControl ParentControl
        {
            get
            {
                return _parentControl;
            }
            
            set
            {
                _parentControl = value;

                if (ParentControlChanged != null)
                    ParentControlChanged(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Gets a control of a specific type using a specific selector (may be cached)
        /// </summary>
        /// <typeparam name="T">The type of the control</typeparam>
        /// <param name="selector">The sQuery selector for the control</param>
        /// <returns>The control</returns>
        public T Get<T>(string selector)
            where T : SlvControl, new()
        {
            return Get((Action<T>)null, selector);
        }

        /// <summary>
        /// Gets a control of a specific type using a specific selector (may be cached)
        /// </summary>
        /// <typeparam name="T">The type of the control</typeparam>
        /// <param name="postCreate">Action to perform on object after control is constructed</param>
        /// <param name="selector">The sQuery selector for the control</param>
        /// <returns>The control</returns>
        public T Get<T>(Action<T> postCreate, string selector)
            where T : SlvControl, new()
        {
            return Get((con, sel) =>
            {
                var result = new T
                {
                    ParentControl = con,
                    MySelector = sel
                };

                if (postCreate != null)
                    postCreate(result);

                return result;
            },
                           selector);

        }

        /// <summary>
        /// Gets a control of a specific type using a specific selector (may be cached)
        /// </summary>
        /// <typeparam name="T">The type of the control</typeparam>
        /// <param name="creator">Function to perform to create the control</param>
        /// <param name="selector">The sQuery selector for the control</param>
        /// <returns>The control</returns>
        public T Get<T>(Func<SlvControlContainer, string, T> creator, string selector)
        {
            var key = string.Format("#{0}#{1}", typeof(T).Name, selector);
            var result = (T)HashControls[key];

            if (Equals(result, default(T)))
            {
                result = creator(this, selector);
                HashControls[key] = result;
            }

            return result;
        }

        /// <summary>
        /// Returns a new SlvControl for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvControl</returns>
        public SlvControl GetControl(string selector)
        {
            return Get<SlvControl>(selector);
        }

        /// <summary>
        /// Returns a new SlvComboBox for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvComboBox</returns>
        public SlvComboBox GetComboBox(string selector)
        {
            return Get<SlvComboBox>(selector);
        }

        /// <summary>
        /// Returns a new SlvHtmlEditor for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvHtmlEditor</returns>
        public SlvHtmlEditor GetHtmlEditor(string selector)
        {
            return Get<SlvHtmlEditor>(selector);
        }

        /// <summary>
        /// Returns a new SlvListBox for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvListBox</returns>
        public SlvListBox GetListBox(string selector)
        {
            return Get<SlvListBox>(selector);
        }

        /// <summary>
        /// Returns a new SlvCheckBox for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvCheckBox</returns>
        public SlvCheckBox GetCheckBox(string selector)
        {
            return Get<SlvCheckBox>(selector);
        }

        /// <summary>
        /// Returns a new SlvRadioButton for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvRadioButton</returns>
        public SlvRadioButton GetRadioButton(string selector)
        {
            return Get<SlvRadioButton>(selector);
        }

        /// <summary>
        /// Returns a new SlvHyperlinkButton for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvHyperlinkButton</returns>
        public SlvHyperlinkButton GetHyperlinkButton(string selector)
        {
            return Get<SlvHyperlinkButton>(selector);
        }

        /// <summary>
        /// Returns a new SlvHyperlinkButton for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <param name="text">text of the link</param>
        /// <returns>A new SlvHyperlinkButton</returns>
        public SlvHyperlinkButton GetHyperlinkButtonByNameAndText(string selector, string text)
        {
            return Get<SlvHyperlinkButton>(selector + "[text*=" + text + "]");
        }

        /// <summary>
        /// Returns a new SlvImage for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvImage</returns>
        public SlvImage GetImage(string selector)
        {
            return Get<SlvImage>(selector);
        }

        /// <summary>
        /// Returns a new SlvButton for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvButton</returns>
        public SlvButton GetButton(string selector)
        {
            return Get<SlvButton>(selector);
        }

        /// <summary>
        /// Returns a new SlvComboBox for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvComboBox</returns>
        public SlvDataGrid GetDataGrid(string selector)
        {
            return Get<SlvDataGrid>(selector);
        }

        /// <summary>
        /// Returns a new SlvComboBox for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvComboBox</returns>
        public SlvItemsControl GetItemsControl(string selector)
        {
            return Get<SlvItemsControl>(selector);
        }

        /// <summary>
        /// Returns a new SlvTextBlock for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvTextBlock</returns>
        public SlvTextBlock GetTextBlock(string selector)
        {
            return Get<SlvTextBlock>(selector);
        }

        /// <summary>
        /// Returns a new SlvTabControl for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvTabControl</returns>
        public SlvTabControl GetTabControl(string selector)
        {
            return Get<SlvTabControl>(selector);
        }

        /// <summary>
        /// Returns a new SlvScrollViewer for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvScrollViewer</returns>
        public SlvScrollViewer GetScrollViewer(string selector)
        {
            return Get<SlvScrollViewer>(selector);
        }

        /// <summary>
        /// Returns a new SlvFilePicker for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvFilePicker</returns>
        public SlvFilePicker GetFilePicker(string selector)
        {
            return Get<SlvFilePicker>(selector);
        }

        /// <summary>
        /// Returns a new SlvControl for the given name
        /// </summary>
        /// <param name="contentpattern">Content regex</param>
        /// <returns>A new SlvControl</returns>
        public SlvControl GetControlByContent(string contentpattern)
        {
            return Get<SlvControl>("[text*=" + contentpattern + "]");
        }

        /// <summary>
        /// Returns a new SlvControl for the given name and content
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <param name="contentpattern">Content regex</param>
        /// <returns>A new SlvControl</returns>
        public SlvControl GetControlByNameAndContent(string selector, string contentpattern)
        {
            return Get<SlvControl>(selector + "[text*=" + contentpattern + "]");
        }

        /// <summary>
        /// Returns a new SlvDateTimePicker for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvDateTimePicker</returns>
        public SlvDateTimePicker GetDateTimePicker(string selector)
        {
            return Get<SlvDateTimePicker>(selector);
        }

        /// <summary>
        /// Returns a new SlvTextBox for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvTextBox</returns>
        public SlvTextBox GetTextBox(string selector)
        {
            return Get<SlvTextBox>(selector); 
        }

        /// <summary>
        /// Returns a new SlvTextBox for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvTextBox</returns>
        public SlvAutoCompleteBox GetAutoCompleteBox(string selector)
        {
            return Get<SlvAutoCompleteBox>(selector);
        }

        /// <summary>
        /// Returns a new SlvCalendar for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvCalendar</returns>
        public SlvCalendar GetCalendar(string selector)
        {
            return Get<SlvCalendar>(selector);
        }

        /// <summary>
        /// Returns a new SlvAccordion for the given name
        /// </summary>
        /// <param name="selector">the sQuery selector of the control</param>
        /// <returns>A new SlvAccordion</returns>
        public SlvAccordion GetAccordion(string selector)
        {
            return Get<SlvAccordion>(selector);
        }

        /// <summary>
        /// Returns a new SlvNumericBox for the given name
        /// </summary>
        /// <param name="selector">The sQuery selector for the control</param>
        /// <returns>A new SlvNumericBox</returns>
        public SlvNumericBox GetNumericBox(string selector)
        {
            return Get<SlvNumericBox>(selector);
        }
    }
}
